#include "GraphManager.h"

GraphManager::GraphManager(Graph* graph)
{
	setGraph(graph);

}

GraphManager::~GraphManager(void)
{
}

List* GraphManager::getShortestPathFromEdges(string edgeIdFrom, string edgeIdTo) {
	// Get the next Vertex from the edge from
	Edge* edgeFrom = getGraph()->getEdge(edgeIdFrom);
	// Get the previous Vertex from the edge to
	Edge* edgeTo = getGraph()->getEdge(edgeIdTo);
	if (edgeFrom == null || edgeTo == null) {
		return null; 
	}
	string vertexFrom = edgeFrom->getIdTo();
	string vertexTo = edgeTo->getIdFrom();
	getGraph()->setDistanceWeightMode();
	return runDijkstra(vertexFrom.c_str(), vertexTo.c_str());
}

List* GraphManager::getFastestPathFromEdges(string edgeIdFrom, string edgeIdTo) {
	// Get the next Vertex from the edge from
	Edge* edgeFrom = getGraph()->getEdge(edgeIdFrom);
	// Get the previous Vertex from the edge to
	Edge* edgeTo = getGraph()->getEdge(edgeIdTo);
	if (edgeFrom == null || edgeTo == null) {
		return null; 
	}
	string vertexFrom = edgeFrom->getIdTo();
	string vertexTo = edgeTo->getIdFrom();
	getGraph()->setTimeWeightMode();
	return runDijkstra(vertexFrom.c_str(), vertexTo.c_str());
}

List* GraphManager::getClosestPathFromEdgeToBuilding(string edgeIdFrom, int from, List* mapObjects,MapObject &near) {
	if( mapObjects == NULL || mapObjects->getSize() <= 0){
		return null;
	}
	Edge* edgeFrom = getGraph()->getEdge(edgeIdFrom);
	if(edgeFrom == null){
		return null;
	}
	int sameStreet = 0;
	getGraph()->setDistanceWeightMode();
	List* ret;
	MapObject* nearest ;
	float partialTime = 0.0;
	for(int i = 0; i < mapObjects->getSize(); i++){
		List* aux;
		// Get the previous Vertex from the edge to
		MapObjectNode* node = (MapObjectNode*) mapObjects->get(i);
		MapObject* mapObject =  node->getNodeValue();
		Edge* edgeTo = getEgdeForBuildingPath(getGraph()->getEdge(mapObject->getIdEdge()), mapObject->getNumber());
		
		if(edgeFrom->getId().compare(edgeTo->getId()) == 0){
			if((edgeFrom->getLastAddress() > edgeFrom->getInitAddress() && (mapObject->getNumber() - from) > 0)
				|| (edgeFrom->getLastAddress() < edgeFrom->getInitAddress() && (mapObject->getNumber() - from) < 0)){
				if(!sameStreet){
					ret = null;
					nearest = mapObject;
					sameStreet = 1;
				}else{
					if((edgeFrom->getLastAddress() > edgeFrom->getInitAddress() && (mapObject->getNumber() - from) > 0) && mapObject->getNumber() < nearest->getNumber()){
						nearest = mapObject;
					}
					if((edgeFrom->getLastAddress() < edgeFrom->getInitAddress() && (mapObject->getNumber() - from) < 0) && mapObject->getNumber() > nearest->getNumber()){
						nearest = mapObject;
					}
				}
			}
		}
		
		
		
		if (!sameStreet && edgeFrom != null && edgeTo != null) {
			string vertexFrom = edgeFrom->getIdTo();
			string vertexTo = edgeTo->getIdFrom();
			if(i == 0){
				ret = runDijkstra(vertexFrom.c_str(), vertexTo.c_str());
				nearest = mapObject;
				partialTime = calculateTotalTime(ret,1);
			}else{
				aux = runDijkstra(vertexFrom.c_str(), vertexTo.c_str());
				float auxTime =  calculateTotalTime(aux,1);
				if(partialTime >= auxTime){
					if(partialTime == auxTime){
						Edge* auxEdge = getGraph()->getEdge(nearest->getIdEdge());
						string s = auxEdge->getId();
						if(s.compare(edgeTo->getId()) == 0){
							if((edgeTo->getLastAddress() > edgeTo->getInitAddress()) && (mapObject->getNumber() < nearest->getNumber())){
								ret = aux;
								partialTime  = auxTime;
								nearest = mapObject;
							}
							if((edgeTo->getLastAddress() < edgeTo->getInitAddress()) && (mapObject->getNumber() > nearest->getNumber())){
								ret = aux;
								partialTime  = auxTime;
								nearest = mapObject;
							}
						}
					}else{
						ret = aux;
						partialTime  = auxTime;
						nearest = mapObject;
					}
				}
			}
		}
	}
	near = *nearest;
	return ret;
}

List* GraphManager::getFastestPathFromEdgeToBuilding(string edgeIdFrom, int from, List* mapObjects,MapObject &near) {
	if( mapObjects == NULL || mapObjects->getSize() <= 0){
		return null;
	}
	Edge* edgeFrom = getGraph()->getEdge(edgeIdFrom);
	if(edgeFrom == null){
		return null;
	}
	getGraph()->setTimeWeightMode();
	int sameStreet = 0;
	List* ret;
	MapObject* nearest;
	float partialTime = 0.0;
	for(int i = 0; i < mapObjects->getSize(); i++){
		List* aux;
		// Get the previous Vertex from the edge to
		MapObjectNode* node = (MapObjectNode*) mapObjects->get(i);
		MapObject* mapObject =  node->getNodeValue();
		Edge* edgeTo =  getEgdeForBuildingPath(getGraph()->getEdge(mapObject->getIdEdge()), mapObject->getNumber());
		
		if(edgeFrom->getId().compare(edgeTo->getId()) == 0){
			if((edgeFrom->getLastAddress() > edgeFrom->getInitAddress() && (mapObject->getNumber() - from) > 0)
				|| (edgeFrom->getLastAddress() < edgeFrom->getInitAddress() && (mapObject->getNumber() - from) < 0)){
				if(!sameStreet){
					ret = null;
					nearest = mapObject;
					sameStreet = 1;
				}else{
					if((edgeFrom->getLastAddress() > edgeFrom->getInitAddress() && (mapObject->getNumber() - from) > 0) && mapObject->getNumber() < nearest->getNumber()){
						nearest = mapObject;
					}
					if((edgeFrom->getLastAddress() < edgeFrom->getInitAddress() && (mapObject->getNumber() - from) < 0) && mapObject->getNumber() > nearest->getNumber()){
						nearest = mapObject;
					}
				}
			}
		}
		
		if (!sameStreet && edgeFrom != null && edgeTo != null) {
			string vertexFrom = edgeFrom->getIdTo();
			string vertexTo = edgeTo->getIdFrom();
			if(i == 0){
				ret = runDijkstra(vertexFrom.c_str(), vertexTo.c_str());
				nearest = mapObject;
				partialTime = calculateTotalTime(ret,0);
			}else{
				aux = runDijkstra(vertexFrom.c_str(), vertexTo.c_str());
				float auxTime =  calculateTotalTime(aux,0);
				if(partialTime >= auxTime){
					if(partialTime == auxTime){
						Edge* auxEdge = getGraph()->getEdge(nearest->getIdEdge());
						string s = auxEdge->getId();
						if(s.compare(edgeTo->getId()) == 0){
							if((edgeTo->getLastAddress() > edgeTo->getInitAddress()) && (mapObject->getNumber() < nearest->getNumber())){
								ret = aux;
								partialTime  = auxTime;
								nearest = mapObject;
							}
							if((edgeTo->getLastAddress() < edgeTo->getInitAddress()) && (mapObject->getNumber() > nearest->getNumber())){
								ret = aux;
								partialTime  = auxTime;
								nearest = mapObject;
							}
						}
					}else{
						ret = aux;
						partialTime  = auxTime;
						nearest = mapObject;
					}
				}
			}
		}
	}
	near = *nearest;
	return ret;
}

List* GraphManager::getShortestPath(string vertexFrom, string vertexTo) {
	getGraph()->setDistanceWeightMode();
	return runDijkstra(vertexFrom, vertexTo);
}

List* GraphManager::getFastestPath(string vertexFrom, string vertexTo) {
	getGraph()->setTimeWeightMode();
	return runDijkstra(vertexFrom, vertexTo);
}

List* GraphManager::runDijkstra(string vertexFrom, string vertexTo) {
	Vertex* vertex = getGraph()->getVertex(vertexFrom);
	Vertex* targetVertex = getGraph()->getVertex(vertexTo);
	if (vertex == null || targetVertex == null) {
		return null;
	}

	List* vertexList = getGraph()->getVertexList();
	// Gets size for arrays
	int n = vertexList->getSize();
	// Creates arrays
	float (*distance)[2];
	distance = new float[n][2];
	initializeDistances(distance, n);
	bool* checked = new bool[n];
	initializeChecks(checked, n);
	// Sets distance for near nodes
	for (int i = 0; i < vertex->getEdgesList()->getSize(); i++) {
		Edge* nearEdge = ((EdgeNode*)vertex->getEdgesList()->get(i))->getNodeValue();
		// Holds the weight of the connection between device i and root device of the method
		distance[getGraph()->getIndexOfVertex(getGraph()->getVertex(nearEdge->getIdTo()))][WEIGHT_COL] = getGraph()->getEdgeWeight(nearEdge);
		// Holds the index of the device whose weight is stored in the first field of the array
		distance[getGraph()->getIndexOfVertex(getGraph()->getVertex(nearEdge->getIdTo()))][VERTEX_ID_COL] = float(getGraph()->getIndexOfVertex(vertex));
	}
	if (vertex->getEdgesList()->getSize() == 0) {
		return new List();
	}
	// Sets distance for current node
	distance[getGraph()->getIndexOfVertex(vertex)][WEIGHT_COL] = 0.0;
	checked[getGraph()->getIndexOfVertex(vertex)] = true;
	// Gets if there's a better way to get to the server
	bool stop = false;
	while (notAllChecked(checked, n) && !stop) {
		Vertex* minimalVertex = getMinimalDistanceVertex(*getGraph(), checked, distance, n);
		if ( minimalVertex == NULL || targetVertex->getId().compare(minimalVertex->getId()) == 0) {
			stop = true;
		} else {
			checked[getGraph()->getIndexOfVertex(minimalVertex)] = true;
			for (int j = 0; j < minimalVertex->getEdgesList()->getSize(); j++) {
				Edge* edge = ((EdgeNode*)minimalVertex->getEdgesList()->get(j))->getNodeValue();
				Vertex* connectedVertex = getGraph()->getVertex(edge->getIdTo());
				if (checked[getGraph()->getIndexOfVertex(connectedVertex)] == false) {
					float alt = distance[getGraph()->getIndexOfVertex(minimalVertex)][WEIGHT_COL] + getGraph()->getEdgeWeight(edge);
					if (alt < distance[getGraph()->getIndexOfVertex(connectedVertex)][WEIGHT_COL] || 
							distance[getGraph()->getIndexOfVertex(connectedVertex)][WEIGHT_COL] == INF) {
						distance[getGraph()->getIndexOfVertex(connectedVertex)][WEIGHT_COL] = alt; 
						distance[getGraph()->getIndexOfVertex(connectedVertex)][VERTEX_ID_COL] = float(getGraph()->getIndexOfVertex(minimalVertex));
					}
				}
			}
		}
	}
	// Creates the routing table for the router
	List* shortestPath = new List();
	createRoutingTable(distance, targetVertex, shortestPath);
	// free memory
	delete [] distance;
	delete [] checked;

	return shortestPath;
}

bool GraphManager::notAllChecked(bool* checked, int size) {
	for (int i = 0; i < size; i++) {
		if (checked[i] == false) {
			return true;
		}
	}
	return false;
}

Vertex* GraphManager::getMinimalDistanceVertex(const Graph& graph, bool* checked, float (*distance)[2], int size) {
	List* vertexList = graph.getVertexList();
	// Save minimal length to a node, and the minimal id who did it.
	float minimalLength = INF;
	string minimalId = "";
	for (int i = 0; i < size; i++) {
		if (checked[i] == false) {
			Vertex* vertex = ((VertexNode*)vertexList->get(i))->getNodeValue();
			if ((minimalLength == INF && distance[i][WEIGHT_COL] != INF) || (minimalLength != INF && distance[i][WEIGHT_COL] < minimalLength && distance[i][WEIGHT_COL] != INF)) {
				minimalLength = distance[i][WEIGHT_COL];
				minimalId = vertex->getId();
			}
		}
	}
	if (minimalId.compare("") == 0) {
		return NULL;
	}

	return graph.getVertex(minimalId);
}

List* GraphManager::createRoutingTable(float (*distance)[2], Vertex* targetVertex, List* shortestPastIndexes) {
	Vertex* target = targetVertex;
	VertexNode* node = new VertexNode();
	node->setNodeValue(((VertexNode*)getGraph()->getVertexList()->get(getGraph()->getIndexOfVertex(targetVertex)))->getNodeValue());
	shortestPastIndexes->add(node);
	bool added = false;
	while (distance[getGraph()->getIndexOfVertex(target)][VERTEX_ID_COL] != INF) {
		VertexNode* newNode = new VertexNode();
		int vertexIdx = int(distance[getGraph()->getIndexOfVertex(target)][VERTEX_ID_COL]);
		newNode->setNodeValue(((VertexNode*)getGraph()->getVertexList()->get(vertexIdx))->getNodeValue());
		shortestPastIndexes->add(newNode);
		target = ((VertexNode*)getGraph()->getVertexList()->get(int(distance[getGraph()->getIndexOfVertex(target)][VERTEX_ID_COL])))->getNodeValue();
		added = true;
	}
	// if only the target was added
	if (!added) {
		shortestPastIndexes->remove(0);
	}

	return shortestPastIndexes;
}

void GraphManager::initializeChecks (bool* checked, int n) {
	for (int i = 0; i < n; i++) {
		checked[i] = false;
	}
}

void GraphManager::initializeDistances(float (*distance)[2], int n) {
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < 2; j++) {
			distance[i][j] = INF;
		}
	}
}

Graph* GraphManager::getGraph() const {
	return graph;
}

void GraphManager::setGraph(Graph* graph) {
	this->graph = graph;
}

Edge* GraphManager::getPhantomEdge() const {
	return phantomEdge;
}

void GraphManager::setPhantomEdge(Edge* phantomEdge) {
	this->phantomEdge = phantomEdge;
}

string GraphManager::validateAddres(string path, int number){
	int i = 0;
	string ret = "";
	List * edgesList = graph->getEdgesListByName(path);
	while(i < edgesList->getSize()){
		EdgeNode * edge = ((EdgeNode*)edgesList->get(i));
		if(edge->getNodeValue()->getWayName().compare(path.c_str()) == 0 
			&& ((number <= edge->getNodeValue()->getLastAddress() && number >= edge->getNodeValue()->getInitAddress())||
			(number >= edge->getNodeValue()->getLastAddress() && number <= edge->getNodeValue()->getInitAddress()))){
				if(ret.compare("")== 0) {

					ret = edge->getNodeValue()->getId();
				}else{
					if(edge->getNodeValue()->getLastAddress()> edge->getNodeValue()->getInitAddress() && (number % 2 == 0)) {
						return edge->getNodeValue()->getId();
					}else{
						return ret;
					}
				}
		}
		i++;
	}
	
	return ret;
}

void GraphManager::addPhantomEdge(Edge* edge){
	if(edge != NULL){
		graph->createEdge("phantom",edge->getIdTo(),edge->getIdFrom(),edge->getLength(),edge->getMaxVelocity(),edge->getWayType(),edge->getWayName(),edge->getInitAddress(),edge->getLastAddress());
		setPhantomEdge(graph->getEdge("phantom"));
	}
}

void GraphManager::removePhantomEdge(){
	if(getPhantomEdge() != NULL){
		graph->deleteEdge("phantom");
		setPhantomEdge(NULL);
	}
}

float GraphManager::calculateTotalTime(List* vertexListPath, int mode) {
	if(vertexListPath == null){
		return null;
	}
	if (vertexListPath->getSize() == 0) {
		//cout << "No existe conexion entre el vertice de origen y el de destino";
		return null;
	}
	
	float partial = 0.0;
	float total = 0.0;
	float minWeight = 0.0;

	for (int i = vertexListPath->getSize() - 1; i >= 0; i-- ) {
		if(i != vertexListPath->getSize() - 1){
			VertexNode* temp = ((VertexNode*)vertexListPath->get(i+1));
			Vertex* ssss = temp->getNodeValue();
			string currentId = ((VertexNode*)vertexListPath->get(i))->getNodeValue()->getId();
			
			for(int j = 0; j < temp->getNodeValue()->getEdgesList()->getSize(); j++){
				EdgeNode * edge = ((EdgeNode*)temp->getNodeValue()->getEdgesList()->get(j));
				if(edge->getNodeValue()->getIdTo().compare(currentId) == 0){
					if(minWeight == 0.0){
						minWeight = graph->getEdgeWeight(edge->getNodeValue());
						if(mode == 0){
							partial = edge->getNodeValue()->getTime();
						}else{
							partial = edge->getNodeValue()->getLength();
						}
					}else{
						float newWeight = graph->getEdgeWeight(edge->getNodeValue());
						if(newWeight < minWeight){
							minWeight = newWeight;
							if(mode == 0){
								partial = edge->getNodeValue()->getTime();
							}else{
								partial = edge->getNodeValue()->getLength();
							}
						}
					}
				}
				total = partial + total;
				partial = 0.0;
				minWeight = 0.0;
			}
		}
	}
	return total;
}

Edge* GraphManager::getEgdeForBuildingPath(Edge* originalEdge, int number){
	List* edgeList = getGraph()->getEdgesListByName(originalEdge->getId());
	string street = originalEdge->getWayName();
	Edge* ret = null;
	int i = 0;
	List * edgesList = graph->getEdgesListByName(street);
	if(edgesList->getSize() == 1){
		Edge* aux = ((EdgeNode*)edgesList->get(i))->getNodeValue();
		if(aux->getWayName().compare(street.c_str()) == 0 
			&& ((number <= aux->getLastAddress() && number >= aux->getInitAddress())||(number >= aux->getLastAddress() && number <= aux->getInitAddress()))){
			return aux;
		}
	}
	while(i < edgesList->getSize()){
		EdgeNode * edge = ((EdgeNode*)edgesList->get(i));
		if(edge->getNodeValue()->getWayName().compare(street.c_str()) == 0 
			&& ((number <= edge->getNodeValue()->getLastAddress() && number >= edge->getNodeValue()->getInitAddress())||
			(number >= edge->getNodeValue()->getLastAddress() && number <= edge->getNodeValue()->getInitAddress()))){
			ret = edge->getNodeValue();
			if(( edge->getNodeValue()->getLastAddress() > edge->getNodeValue()->getInitAddress()) && ((number - edge->getNodeValue()->getInitAddress()) < (abs( edge->getNodeValue()->getLastAddress() - edge->getNodeValue()->getInitAddress() )/2.0))) {
				return edge->getNodeValue();
			}
			if(( edge->getNodeValue()->getLastAddress() < edge->getNodeValue()->getInitAddress()) && ((number - edge->getNodeValue()->getLastAddress()) > (abs( edge->getNodeValue()->getLastAddress() - edge->getNodeValue()->getInitAddress())/2.0))) {
				return edge->getNodeValue();
			}
		}
		i++;
	}
	
	return ret;
}

